﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace avs2bdnxml_gui
{
    public partial class MainForm : Form
    {
        private static readonly List<Common.TaskData> TaskDataList = new List<Common.TaskData>();
        private static readonly HashConfig Config = new HashConfig(Directory.GetCurrentDirectory() + "\\config.dat");
        private static readonly string Avs2Bdnxmlpath = Directory.GetCurrentDirectory() + "\\core\\avs2bdnxml.exe";
        private static readonly string Vsfilterpath = Directory.GetCurrentDirectory() + "\\core\\VSFilter.dll";
        private static readonly string Vsfiltermodpath = Directory.GetCurrentDirectory() + "\\core\\VSFilterMod.dll";
        private static Shell _shell;
        private static int _lastTaskIndex = -1;
        private int _totalFrame;
        private int _fi;

        private void LoadConfig()
        {
            Config.LoadFromFile();
            var cdata = (Common.ConfigData)Config.ReadKeyO("Config", "Default");

            if (cdata != null)
            {
                UpdateUi(cdata);
            }
        }

        private void SaveConfig()
        {
            var cdata = GetCurrentConfig();
            Config.WriteKey("Config", "Default", cdata);
            Config.SaveFromFile();
        }

        private void UpdateUi(Common.ConfigData cdata)
        {
            cbxresolution.SelectedIndex = cdata.ResolutionIndex;
            cbxfps.SelectedIndex = cdata.FpsIndex;
            tbxlang.Text = cdata.Lang;
            tbxoutpath.Text = cdata.OutputPath;
            num_x.Value = cdata.X;
            num_y.Value = cdata.Y;
            num_s.Value = cdata.S;
            num_m.Value = cdata.M;
            MaxDegreeOfParallelism.Value = cdata.MaxDegreeOfParallelism;
            num_eblacka.Value = cdata.EBlacka;
            cbx_a.Checked = cdata.A;
            cbx_p.Checked = cdata.P;
            cbx_b.Checked = cdata.B;
            cbxensup.Checked = cdata.BSup;
            cbxfblk.Checked = cdata.BFirstBlack;
            cbxeblk.Checked = cdata.BEndBlack;
            cbxenvsmod.Checked = cdata.BvsMod;
        }

        private void OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            this.Invoke(new MethodInvoker(() => { ParseProgress(e.Data); }));
        }

        private void ParseProgress(string str)
        {
            if (str == null)
            {
                return;
            }

            if (str.IndexOf("Progress", StringComparison.Ordinal) < 0)
            {
                return;
            }

            var sa = str.Split(' ');

            if (sa.Length < 5) return;
            var tstr = sa[1];
            var fi = int.Parse(tstr.Split('/')[0]);
            var ti = int.Parse(tstr.Split('/')[1]);
            if (_fi < fi)
            {
                _fi = fi;
            }

            this.Invoke(new MethodInvoker(() =>
            {
                //procc.Maximum = ti;
                procc.Maximum = _totalFrame;
                procc.Value = _fi;
            }));
        }

        public void SetUi(bool b)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                procc.Value = 0;
                procc.Maximum = 0;
                proct.Value = 0;
                //_totalFrame = 0;
                //_fi = 0;

                if (b)
                {
                    btnstart.Enabled = true;
                    btnstop.Enabled = false;
                    btnexit.Enabled = true;
                    grbtaskdata.Enabled = true;
                    grbtasklst.Enabled = true;
                }
                else
                {
                    btnstart.Enabled = false;
                    btnexit.Enabled = false;
                    grbtaskdata.Enabled = false;
                    grbtasklst.Enabled = false;
                    btnstop.Enabled = true;
                }
            }));
        }

        private Common.ConfigData GetCurrentConfig()
        {
            var cdata = new Common.ConfigData();

            if (tbxoutpath.Text.Trim() == "")
            {
                tbxoutpath.Text = Directory.GetCurrentDirectory();
            }

            cdata.ResolutionIndex = cbxresolution.SelectedIndex;
            cdata.FpsIndex = cbxfps.SelectedIndex;
            cdata.Lang = tbxlang.Text;
            cdata.X = (int)num_x.Value;
            cdata.Y = (int)num_y.Value;
            cdata.S = (int)num_s.Value;
            cdata.M = (int)num_m.Value;
            cdata.MaxDegreeOfParallelism = (int)MaxDegreeOfParallelism.Value;
            cdata.A = cbx_a.Checked;
            cdata.P = cbx_p.Checked;
            cdata.B = cbx_b.Checked;
            cdata.EBlacka = (int)num_eblacka.Value;
            cdata.BSup = cbxensup.Checked;
            cdata.BFirstBlack = cbxfblk.Checked;
            cdata.BEndBlack = cbxeblk.Checked;
            cdata.BvsMod = cbxenvsmod.Checked;
            cdata.OutputPath = tbxoutpath.Text;
            return cdata;
        }

        private void AddTask(string fname)
        {
            lbxtask.Items.Add(Path.GetFileName(fname) ?? throw new InvalidOperationException());
            var tdata = new Common.TaskData
            {
                CData = GetCurrentConfig(),
                FileFullPath = fname,
                FileName = Path.GetFileName(fname)
            };
            TaskDataList.Add(tdata);
        }

        private void RemoveTask(int index)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                TaskDataList.RemoveAt(index);
                lbxtask.Items.RemoveAt(index);
            }));
        }

        private void RemoveTask(Common.TaskData tdata)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                TaskDataList.Remove(tdata);
                foreach (var lbxtaskItem in lbxtask.Items)
                {
                    if (!tdata.FileName.Equals(lbxtaskItem)) continue;
                    lbxtask.Items.Remove(lbxtaskItem);
                    break;
                }
            }));
        }

        private void MakeAvs(Common.TaskData tdata)
        {
            //var tdata = TaskDataList[index];
            var olst = new List<string>();
            var filemainname = Path.GetFileNameWithoutExtension(tdata.FileName);
            var outputpath = tdata.CData.OutputPath + "\\" + filemainname + "\\";
            var fps = Common.FpsList[tdata.CData.FpsIndex];
            var res = Common.ResolutionList[tdata.CData.ResolutionIndex];
            var ftype = Common.GetFileType(tdata.FileFullPath);
            FileManagement.ClearDir(outputpath);

            int fcount;
            switch (ftype)
            {
                case Common.FileType.Ass:
                    fcount = AssFile.GetFrameCount(tdata.FileFullPath, fps);
                    break;
                case Common.FileType.Ssa:
                    fcount = SsaFile.GetFrameCount(tdata.FileFullPath, fps);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            fcount += 100;
            if (_totalFrame < fcount)
            {
                _totalFrame = fcount;
            }

            if (tdata.CData.BvsMod)
            {
                olst.Add("LoadPlugin(\"" + Vsfiltermodpath + "\")");
                olst.Add("MaskSubMod(\"" + tdata.FileFullPath + "\", " + res.X + ", " + res.Y +
                         ", " + fps + ", " + fcount + ")");
            }
            else
            {
                olst.Add("LoadPlugin(\"" + Vsfilterpath + "\")");
                olst.Add("MaskSub(\"" + tdata.FileFullPath + "\", " + res.X + ", " + res.Y +
                         ", " + fps + ", " + fcount + ")");
            }

            olst.Add("LanczosResize(" + res.X + ", " + res.Y + ")");

            if (!Directory.Exists(tdata.CData.OutputPath))
            {
                Directory.CreateDirectory(tdata.CData.OutputPath);
            }

            var avsfile = outputpath + filemainname + ".avs";

            if (!Directory.Exists(outputpath))
            {
                Directory.CreateDirectory(outputpath);
            }

            if (File.Exists(avsfile))
            {
                File.Delete(avsfile);
            }

            var fs = File.Create(avsfile);
            var sr = new StreamWriter(fs) { AutoFlush = true };

            foreach (var s in olst)
            {
                sr.WriteLine(s);
            }

            sr.Close();
            fs.Close();
        }

        private void Btnstart_Click(object sender, EventArgs e)
        {
            if (lbxtask.Items.Count == 0)
            {
                return;
            }

            if (!File.Exists(Avs2Bdnxmlpath) || !File.Exists(Vsfilterpath) || !File.Exists(Vsfiltermodpath))
            {
                MessageBox.Show(this, @"找不到核心文件!", @"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            _totalFrame = 0;
            _fi = 0;
            proct.Maximum = lbxtask.Items.Count;
            SetUi(false);
            worker.RunWorkerAsync();
        }

        private void Btnstop_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(this, @"任务处理中 是否强制结束?", @"询问", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) !=
                DialogResult.OK) return;
            if (worker.IsBusy)
            {
                worker.CancelAsync();
                _shell.BatchStop();
                lbxtask.Items.Clear();
            }

            SetUi(true);
        }

        private void Btnbrowser_Click(object sender, EventArgs e)
        {
            var fbd = new FolderBrowserDialog { ShowNewFolderButton = true };
            fbd.ShowDialog(this);

            if (fbd.SelectedPath != "")
            {
                tbxoutpath.Text = fbd.SelectedPath;
            }
        }

        public MainForm()
        {
            _fi = 0;
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
            this.nicon.Icon = this.Icon;
            cbxresolution.SelectedIndex = 0;
            cbxfps.SelectedIndex = 0;
            LoadConfig();
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason != CloseReason.UserClosing) return;
            this.WindowState = FormWindowState.Minimized;
            e.Cancel = true;
            this.Hide();
        }

        private void Lbxtask_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void Lbxtask_DragDrop(object sender, DragEventArgs e)
        {
            var sa = (string[])e.Data.GetData(DataFormats.FileDrop);

            foreach (var s in sa)
            {
                var ext = Path.GetExtension(s)?.ToUpper();

                if (ext == ".ASS" || ext == ".SSA")
                {
                    AddTask(s);
                }
            }
        }

        private void Lbxtask_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            if (lbxtask.SelectedItems.Count > 0)
            {
                SaveConfig();

                if (_lastTaskIndex >= 0)
                {
                    TaskDataList[_lastTaskIndex].CData = GetCurrentConfig();
                }

                lbltasksel.Text = (string)lbxtask.Items[lbxtask.SelectedIndex];
                var tdata = TaskDataList[lbxtask.SelectedIndex];
                UpdateUi(tdata.CData);
                _lastTaskIndex = lbxtask.SelectedIndex;
            }
            else
            {
                lbltasksel.Text = @"全局设置";
            }
        }

        private void ReLoadConfig(Common.ConfigData cdata)
        {
            cdata.ResolutionIndex = cbxresolution.SelectedIndex;
            cdata.FpsIndex = cbxfps.SelectedIndex;
            cdata.Lang = tbxlang.Text;
            cdata.OutputPath = tbxoutpath.Text;
            cdata.X = (int)num_x.Value;
            cdata.Y = (int)num_y.Value;
            cdata.S = (int)num_s.Value;
            cdata.M = (int)num_m.Value;
            cdata.MaxDegreeOfParallelism = (int)MaxDegreeOfParallelism.Value;
            cdata.EBlacka = (int)num_eblacka.Value;
            cdata.A = cbx_a.Checked;
            cdata.P = cbx_p.Checked;
            cdata.B = cbx_b.Checked;
            cdata.BSup = cbxensup.Checked;
            cdata.BFirstBlack = cbxfblk.Checked;
            cdata.BEndBlack = cbxeblk.Checked;
            cdata.BvsMod = cbxenvsmod.Checked;
        }

        private void Tmnuremove_Click(object sender, EventArgs e)
        {
            if (lbxtask.Items.Count > 0 && lbxtask.SelectedItems.Count > 0)
            {
                RemoveTask(lbxtask.SelectedIndex);
            }
        }

        private void Tmnuclear_Click(object sender, EventArgs e)
        {
            lbxtask.Items.Clear();
            TaskDataList.Clear();
        }

        private void Tmnushow_Click(object sender, EventArgs e)
        {
            if (!this.Visible)
            {
                this.Show();
            }
        }

        private void Tmnuexit_Click(object sender, EventArgs e)
        {
            if (worker.IsBusy)
            {
                if (MessageBox.Show(this, @"任务处理中 您确定要终止并退出吗?", @"询问", MessageBoxButtons.OKCancel,
                        MessageBoxIcon.Question) != DialogResult.OK) return;

                worker.CancelAsync();
                _shell.BatchStop();
            }

            SaveConfig();
            Application.ExitThread();
            Application.Exit();
        }

        private void Nicon_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            if (this.Visible) return;
            this.Show();
            this.WindowState = FormWindowState.Normal;
            this.Focus();
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var i = 0;
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            if (TaskDataList.Count > 0)
            {
                Parallel.ForEach(TaskDataList,
                    new ParallelOptions { MaxDegreeOfParallelism = (int)MaxDegreeOfParallelism.Value }, tdata =>
                     {
                         this.ReLoadConfig(tdata.CData);
                         var workpath = tdata.CData.OutputPath;
                         var filemainname = Path.GetFileNameWithoutExtension(tdata.FileName);
                         var resolution = (string)cbxresolution.Items[tdata.CData.ResolutionIndex];
                         var fps = (string)cbxfps.Items[tdata.CData.FpsIndex];
                         string _x = "", _y = "", _s = "", _m = "", _a = "", _b = "", _p = "", sup = "";
                         this.MakeAvs(tdata);
                         if (tdata.CData.X > 0)
                         {
                             _x = " -x" + tdata.CData.X + " ";
                         }

                         if (tdata.CData.Y > 0)
                         {
                             _x = " -y" + tdata.CData.Y + " ";
                         }

                         if (tdata.CData.S > 0)
                         {
                             _s = " -s" + tdata.CData.S + " ";
                         }

                         if (tdata.CData.M > 0)
                         {
                             _m = " -m" + tdata.CData.M + " ";
                         }

                         if (tdata.CData.A)
                         {
                             _a = " -a1 ";
                         }

                         if (tdata.CData.B)
                         {
                             _b = " -b1 ";
                         }

                         if (tdata.CData.P)
                         {
                             _p = " -p1 ";
                         }

                         if (tdata.CData.BSup)
                         {
                             sup = " -o \"" + workpath + "\\" + filemainname + "\\" + filemainname + ".sup\" ";
                         }

                         string param;

                         if (sup.Trim() != "")
                         {
                             param = "-t \"" + filemainname + "\" -l " + tdata.CData.Lang + " -v " + resolution +
                                     " -f " +
                                     fps + _x + _y + _s + _m + _a + _b + _p + "-n1 -o \"" + workpath + "\\" +
                                     filemainname +
                                     "\\" + filemainname + ".xml\"" + sup + "\"" + workpath + "\\" + filemainname +
                                     "\\" +
                                     filemainname + ".avs\"";
                         }
                         else
                         {
                             param = "-t \"" + filemainname + "\" -l " + tdata.CData.Lang + " -v " + resolution +
                                     " -f " +
                                     fps + _x + _y + _s + _m + _a + _b + _p + "-n1 -o \"" + workpath + "\\" +
                                     filemainname +
                                     "\\" + filemainname + ".xml\" \"" + workpath + "\\" + filemainname + "\\" +
                                     filemainname + ".avs\"";
                         }

                         try
                         {
                             _shell = new Shell(Avs2Bdnxmlpath, param, workpath, OutputDataReceived);
                             _shell.Start();

                             while (!_shell.HasExited)
                             {
                                 Thread.Sleep(10);

                                 if (!worker.CancellationPending) continue;
                                 SetUi(true);
                                 _shell.Stop();
                                 e.Cancel = true;
                                 return;
                             }
                         }
                         catch (InvalidOperationException invalidOperation)
                         {
                             Console.WriteLine(@"shell:" + invalidOperation.Message);
                             MessageBox.Show(this, invalidOperation.Message, @"提示", MessageBoxButtons.OK,
                                 MessageBoxIcon.Information);
                         }

                         try
                         {
                             if (tdata.CData.BFirstBlack)
                             {
                                 var bdn = new Bdn(workpath + "\\" + filemainname + "\\" + filemainname + ".xml",
                                     Common.FpsList[tdata.CData.FpsIndex],
                                     Common.ResolutionList[tdata.CData.ResolutionIndex]);
                                 bdn.InsertBlack(true, workpath + "\\" + filemainname + "\\" + "0.png");
                                 bdn.SaveBdn();
                             }

                             if (tdata.CData.BEndBlack)
                             {
                                 var bdn = new Bdn(workpath + "\\" + filemainname + "\\" + filemainname + ".xml",
                                     Common.FpsList[tdata.CData.FpsIndex],
                                     Common.ResolutionList[tdata.CData.ResolutionIndex]);
                                 bdn.InsertBlack(false, workpath + "\\" + filemainname + "\\" + "0.png",
                                     tdata.CData.EBlacka);
                                 bdn.SaveBdn();
                             }
                         }
                         catch (FileNotFoundException fileNotFound)
                         {
                             if (worker.IsBusy)
                             {
                                 worker.CancelAsync();
                             }

                             Console.WriteLine(@"bdn:" + fileNotFound.Message);
                             MessageBox.Show(this, fileNotFound.Message, @"提示", MessageBoxButtons.OK,
                                 MessageBoxIcon.Information);
                         }

                         RemoveTask(tdata);
                         ++i;
                         worker.ReportProgress(i);
                     });
            }

            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds / 1000); //本次MyFunc()方法的运行毫秒数

            SetUi(true);
            MessageBox.Show(this, @"全部操作完成，用时：" + stopwatch.ElapsedMilliseconds / 1000 + @" 秒", @"提示",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
            //重置计时器
            stopwatch.Restart(); //此处可以使用stopwatch.Reset(); stopwatch.Start();组合代替
        }

        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.Invoke(new MethodInvoker(() => { proct.Value = e.ProgressPercentage; }));
        }
    }
}